const { ipcMain, app } = require('electron');
const path = require('path');
const fs = require('fs').promises;
const fsSync = require('fs');
const archiver = require('archiver');
const AdmZip = require('adm-zip');
const crypto = require('crypto');
const { getMainKnex, getCaseDatabaseInfo } = require('../database/knex');

function registerCaseHandlers(projectRoot) {
    /**
     * Get all cases
     */
    ipcMain.handle('get-cases', async (event, filter = {}) => {
        try {
            const knex = getMainKnex();
            let query = knex('cases').select('*');

            if (filter) {
                if (filter.caseNumber) {
                    query = query.where('case_number', 'like', `%${filter.caseNumber}%`);
                }
                if (filter.caseName) {
                    query = query.where('case_name', 'like', `%${filter.caseName}%`);
                }
                if (filter.startDate) {
                    query = query.where('case_start_time', '>=', filter.startDate);
                }
                if (filter.endDate) {
                    query = query.where('case_start_time', '<=', `${filter.endDate} 23:59:59`);
                }
            }

            const cases = await query.orderBy('created_time', 'desc');
            return cases;
        } catch (error) {
            console.error('[Get Cases Error]', error);
            throw error;
        }
    });

    /**
     * Create new case
     */
    /**
     * Create new case
     */
    ipcMain.handle('create-case', async (event, caseData) => {
        try {
            const knex = getMainKnex();
            const now = new Date();
            const timestamp = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}`;

            // Get storage path from config
            let storageRoot = projectRoot; // fallback
            try {
                const config = await knex('global_configs').where('key', 'storage_path').first();
                if (config && config.value) {
                    storageRoot = config.value;
                    console.log(`[Create Case] Using configured storage path: ${storageRoot}`);
                } else {
                    console.log(`[Create Case] No storage path configured, using default: ${storageRoot}`);
                }
            } catch (err) {
                console.log(`[Create Case] Failed to read storage config, using default: ${storageRoot}`, err);
            }

            // Create case database path
            const casesDir = path.join(storageRoot, 'cases');
            await fs.mkdir(casesDir, { recursive: true });

            // Retry loop for generating unique case number
            let caseId;
            let caseNumber;
            let retryCount = 0;
            const maxRetries = 5;

            while (retryCount < maxRetries) {
                try {
                    // Get count for sequence number
                    const result = await knex('cases')
                        .where('case_number', 'like', `AJ${timestamp}%`)
                        .count('* as count')
                        .first();

                    const sequenceNum = String((result.count || 0) + 1 + retryCount).padStart(3, '0');
                    caseNumber = `AJ${timestamp}${sequenceNum}`;

                    const caseStartTime = caseData.case_start_time || caseData.created_time || new Date().toISOString().slice(0, 19).replace('T', ' ');
                    const createdTime = new Date().toISOString().slice(0, 19).replace('T', ' ');

                    // Insert case record
                    const [id] = await knex('cases').insert({
                        case_name: caseData.case_name,
                        case_status: caseData.case_status,
                        case_number: caseNumber,
                        created_time: createdTime,
                        case_start_time: caseStartTime,
                        password: caseData.password || null,
                        description: caseData.description || null,
                        db_path: '' // Will update after creating database
                    });
                    caseId = id;
                    break; // Success
                } catch (error) {
                    if (error.message.includes('UNIQUE constraint failed: cases.case_number')) {
                        retryCount++;
                        console.warn(`[Create Case] Case number collision for ${caseNumber}, retrying (${retryCount}/${maxRetries})...`);
                        continue;
                    }
                    throw error; // Other errors
                }
            }

            if (!caseId) {
                throw new Error('Failed to generate unique case number after multiple retries');
            }

            // Create case database and get path
            const dbPath = path.join(casesDir, `${caseNumber}_id${caseId}.db`);
            const { knex: caseKnex } = await getCaseDatabaseInfo(caseId);

            // Update case with database path
            await knex('cases')
                .where('id', caseId)
                .update({ db_path: dbPath });

            return {
                id: caseId,
                case_number: caseNumber,
                db_path: dbPath
            };
        } catch (error) {
            console.error('[Create Case Error]', error);
            throw error;
        }
    });

    /**
     * Update case
     */
    ipcMain.handle('update-case', async (event, caseData) => {
        try {
            const knex = getMainKnex();
            const updateData = {};

            // Build update object based on provided fields
            if (caseData.hasOwnProperty('closed_status') && !caseData.case_name) {
                updateData.closed_status = caseData.closed_status;
            } else if (caseData.hasOwnProperty('password') && !caseData.case_name) {
                updateData.password = caseData.password;
            } else {
                updateData.case_name = caseData.case_name;
                updateData.case_status = caseData.case_status;
                updateData.description = caseData.description || null;
                if (caseData.hasOwnProperty('case_start_time')) {
                    updateData.case_start_time = caseData.case_start_time;
                }
            }

            const changes = await knex('cases')
                .where('id', caseData.id)
                .update(updateData);

            return { changes };
        } catch (error) {
            console.error('[Update Case Error]', error);
            throw error;
        }
    });

    /**
     * Delete case
     */
    ipcMain.handle('delete-case', async (event, caseId) => {
        try {
            const knex = getMainKnex();

            // Get case info before deletion
            const caseInfo = await knex('cases')
                .select('db_path', 'case_number')
                .where('id', caseId)
                .first();

            if (!caseInfo) {
                throw new Error('Case not found');
            }

            // Delete case record
            const changes = await knex('cases')
                .where('id', caseId)
                .delete();

            // Delete case database file
            if (caseInfo.db_path) {
                try {
                    if (await fileExists(caseInfo.db_path)) {
                        await fs.unlink(caseInfo.db_path);
                        console.log(`Deleted case database file: ${caseInfo.db_path}`);
                    }

                    // Delete attachments directory
                    const attachmentsDir = path.join(projectRoot, 'attachments', `case_${caseId}`);
                    if (await fileExists(attachmentsDir)) {
                        await fs.rm(attachmentsDir, { recursive: true, force: true });
                        console.log(`Deleted case attachments: ${attachmentsDir}`);
                    }
                } catch (fsErr) {
                    console.error('Failed to delete case files:', fsErr);
                }
            }

            return { changes };
        } catch (error) {
            console.error('[Delete Case Error]', error);
            throw error;
        }
    });

    /**
     * Export case
     */
    ipcMain.handle('export-case', async (event, caseId, exportOptions = {}) => {
        try {
            const knex = getMainKnex();
            const userDataPath = app.getPath('userData');
            const tempDir = path.join(userDataPath, 'temp_exports');

            await fs.mkdir(tempDir, { recursive: true });

            // Get case info
            const caseInfo = await knex('cases')
                .select('*')
                .where('id', caseId)
                .first();

            if (!caseInfo) throw new Error('案件不存在');
            if (!caseInfo.db_path) throw new Error('案件数据库路径未设置');
            if (!fsSync.existsSync(caseInfo.db_path)) throw new Error('案件数据库文件不存在');

            const caseExportDir = path.join(tempDir, `case_${caseId}`);
            await fs.mkdir(caseExportDir, { recursive: true });

            // Copy database file
            const sourceDbPath = caseInfo.db_path;
            const targetDbPath = path.join(caseExportDir, 'case.db');
            await fs.copyFile(sourceDbPath, targetDbPath);

            // Create attachments directory
            const attachmentsDir = path.join(caseExportDir, 'attachments');
            await fs.mkdir(attachmentsDir, { recursive: true });

            // Create metadata
            const metadata = {
                version: '1.0',
                case_id: `case_${caseId}`,
                case_number: caseInfo.case_number,
                case_name: caseInfo.case_name,
                created_time: caseInfo.created_time,
                export_time: new Date().toISOString(),
                exporter: exportOptions.exporter || 'Unknown',
                description: caseInfo.description,
                encrypted: !!caseInfo.password,
                encryption_method: caseInfo.password ? 'AES-256' : null,
                schema_version: '1.0'
            };

            await fs.writeFile(path.join(caseExportDir, 'metadata.json'), JSON.stringify(metadata, null, 2));

            // Create file manifest
            const manifest = await createFileManifest(caseExportDir);
            await fs.writeFile(path.join(caseExportDir, 'manifest.json'), JSON.stringify(manifest, null, 2));

            // Create package
            const finalFileName = `案件_${caseInfo.case_number}.case`;
            const exportPath = path.join(exportOptions.exportPath || userDataPath, finalFileName);
            await createCasePackage(caseExportDir, exportPath);

            // Cleanup
            await fs.rm(tempDir, { recursive: true, force: true });

            return { success: true, filePath: exportPath, fileName: finalFileName };
        } catch (error) {
            console.error('[Export Case Error]', error);
            throw error;
        }
    });

    /**
     * Import case
     */
    ipcMain.handle('import-case', async (event, importFilePath, importOptions = {}) => {
        try {
            const knex = getMainKnex();
            const userDataPath = app.getPath('userData');
            const tempDir = path.join(userDataPath, 'temp_imports');

            // Get storage path from config
            let storageRoot = projectRoot; // fallback
            try {
                const config = await knex('global_configs').where('key', 'storage_path').first();
                if (config && config.value) {
                    storageRoot = config.value;
                    console.log(`[Import Case] Using configured storage path: ${storageRoot}`);
                }
            } catch (err) {
                console.log(`[Import Case] Failed to read storage config, using default: ${storageRoot}`, err);
            }

            const casesDir = path.join(storageRoot, 'cases');

            await fs.mkdir(tempDir, { recursive: true });

            // Extract package
            const zip = new AdmZip(importFilePath);
            const extractPath = path.join(tempDir, 'extracted');
            zip.extractAllTo(extractPath, true);

            // Verify integrity
            const manifestPath = path.join(extractPath, 'manifest.json');
            if (await fileExists(manifestPath)) {
                const manifestContent = await fs.readFile(manifestPath, 'utf8');
                const manifest = JSON.parse(manifestContent);
                const isValid = await verifyFileIntegrity(extractPath, manifest);
                if (!isValid && !importOptions.ignoreIntegrity) {
                    throw new Error('案件包文件完整性验证失败');
                }
            }

            // Read metadata
            const metadataPath = path.join(extractPath, 'metadata.json');
            const metadataContent = await fs.readFile(metadataPath, 'utf8');
            const metadata = JSON.parse(metadataContent);

            // Check for duplicates
            const existingCase = await knex('cases')
                .where('case_number', metadata.case_number)
                .orWhere(function () {
                    this.where('description', metadata.description)
                        .where('case_name', metadata.case_name);
                })
                .first();

            if (existingCase) {
                throw new Error(`Case "${metadata.case_name}" already exists, cannot import duplicate`);
            }

            // Create new case number
            const now = new Date();
            const timestamp = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, '0')}${String(now.getDate()).padStart(2, '0')}`;
            const result = await knex('cases')
                .where('case_number', 'like', `AJ${timestamp}%`)
                .count('* as count')
                .first();

            const sequenceNum = String((result.count || 0) + 1).padStart(3, '0');
            const newCaseNumber = `AJ${timestamp}${sequenceNum}`;

            // Insert new case
            const [newCaseId] = await knex('cases').insert({
                case_name: metadata.case_name,
                case_status: '初核',
                case_number: newCaseNumber,
                created_time: metadata.created_time,
                case_start_time: metadata.created_time,
                password: metadata.encrypted ? 'encrypted' : null,
                description: metadata.description,
                db_path: ''
            });

            // Copy database file
            const sourceDbPath = path.join(extractPath, 'case.db');
            const targetDbPath = path.join(casesDir, `${newCaseNumber}_id${newCaseId}.db`);
            await fs.copyFile(sourceDbPath, targetDbPath);

            // Update database path
            await knex('cases')
                .where('id', newCaseId)
                .update({ db_path: targetDbPath });

            // Copy attachments
            const sourceAttachmentsDir = path.join(extractPath, 'attachments');
            if (await fileExists(sourceAttachmentsDir)) {
                const targetAttachmentsDir = path.join(projectRoot, 'attachments', `case_${newCaseId}`);
                await fs.mkdir(targetAttachmentsDir, { recursive: true });
                await fs.cp(sourceAttachmentsDir, targetAttachmentsDir, { recursive: true });
            }

            // Cleanup
            await fs.rm(tempDir, { recursive: true, force: true });

            return { success: true, caseId: newCaseId, caseName: metadata.case_name, caseNumber: newCaseNumber };
        } catch (error) {
            console.error('[Import Case Error]', error);
            throw error;
        }
    });
}

// Helper functions
async function fileExists(filePath) {
    try {
        await fs.access(filePath);
        return true;
    } catch {
        return false;
    }
}

async function createFileManifest(directory) {
    const manifest = { files: {}, created: new Date().toISOString() };

    async function scanDirectory(dir, baseDir = dir) {
        const files = await fs.readdir(dir);
        for (const file of files) {
            const filePath = path.join(dir, file);
            const stat = await fs.stat(filePath);
            if (stat.isDirectory()) {
                await scanDirectory(filePath, baseDir);
            } else {
                const relativePath = path.relative(baseDir, filePath);
                const fileHash = await calculateFileHash(filePath);
                manifest.files[relativePath] = {
                    size: stat.size,
                    hash: fileHash,
                    modified: stat.mtime.toISOString()
                };
            }
        }
    }

    await scanDirectory(directory);
    return manifest;
}

async function calculateFileHash(filePath) {
    const hash = crypto.createHash('sha256');
    const stream = fsSync.createReadStream(filePath);

    return new Promise((resolve, reject) => {
        stream.on('data', data => hash.update(data));
        stream.on('end', () => resolve(hash.digest('hex')));
        stream.on('error', reject);
    });
}

async function createCasePackage(sourceDir, outputPath) {
    return new Promise((resolve, reject) => {
        const output = fsSync.createWriteStream(outputPath);
        const archive = archiver('zip', { zlib: { level: 9 } });

        output.on('close', resolve);
        archive.on('error', reject);
        archive.pipe(output);
        archive.directory(sourceDir, false);
        archive.finalize();
    });
}

async function verifyFileIntegrity(directory, manifest) {
    try {
        for (const [relativePath, fileInfo] of Object.entries(manifest.files)) {
            const filePath = path.join(directory, relativePath);
            if (!(await fileExists(filePath))) return false;
            const currentHash = await calculateFileHash(filePath);
            if (currentHash !== fileInfo.hash) return false;
        }
        return true;
    } catch {
        return false;
    }
}

module.exports = { registerCaseHandlers };
